#!/usr/bin/env node
// Copyright (C) 2020  Matthew "strager" Glazar
// See end of file for extended copyright information.

let assert = require("assert");
let path = require("path");
let fs = require("fs");

let BITS_PER_BYTE = 8;

function main() {
  testIsIDStart();
  testIsIDContinue();
  testIsJSIdentifierStart();
  testIsJSIdentifierPart();
  testDumpBitTable();
  testRstripArray();

  let maxCodePoint = 0x10ffff;

  let chunkSize = 256; // Arbitrary. Found to produce the smallest tables.

  let chunkIndexType = "std::uint8_t";
  let maxChunkIndex = (1 << 8) - 1;

  let outputPath = path.join(
    __dirname,
    "..",
    "..",
    "src",
    "quick-lint-js",
    "fe",
    "lex-unicode-generated.cpp"
  );
  console.log(`Creating ${outputPath} ...`);
  let output = fs.createWriteStream(outputPath);
  output.write(
    `\
// Copyright (C) 2020  Matthew "strager" Glazar
// See end of file for extended copyright information.

// This file was generated by tools/generate-lex-unicode/index.js.

#include <cstdint>
#include <quick-lint-js/fe/lex.h>

// clang-format off

namespace quick_lint_js {
`
  );

  output.write(
    `\
static_assert(Lexer::unicode_table_chunk_size == ${chunkSize});
static_assert(std::is_same_v<Lexer::Unicode_Table_Chunk_Index_Type, ${chunkIndexType}>);

`
  );

  let identifierStartData = [];
  let identifierPartData = [];
  for (let codePoint = 0; codePoint <= maxCodePoint; ++codePoint) {
    identifierStartData.push(isJSIdentifierStart(codePoint));
    identifierPartData.push(isJSIdentifierPart(codePoint));
  }

  let chunkDataToIndex /*: Map<BigInt, number> */ = new Map();
  let allChunks = [];

  function boolsToBigInt(bools /*: Array<bool> */) /*: BigInt */ {
    return BigInt(
      "0b0" +
        bools
          .map((b) => (b ? "1" : "0"))
          .reverse()
          .join("")
    );
  }

  function seeChunk(chunkData /*: Array<bool> */) {
    let key = boolsToBigInt(chunkData);
    let chunkIndex = chunkDataToIndex.get(key);
    if (chunkIndex === undefined) {
      chunkIndex = chunkDataToIndex.size;
      chunkDataToIndex.set(key, chunkIndex);
      allChunks.push(...chunkData);
    }
    return chunkIndex;
  }

  for (let data of [identifierStartData, identifierPartData]) {
    for (let c of chunk(data, chunkSize)) {
      seeChunk(c);
    }
  }
  let zerosChunkIndex = seeChunk([]);

  assert.ok(chunkDataToIndex.size <= maxChunkIndex);

  output.write("const std::uint8_t Lexer::unicode_tables_chunks[] = {\n");
  output.write(dumpBitTableToString(allChunks, { indentation: "  " }));
  console.log(`unicode_tables_chunks_size = ${allChunks.length}`);
  output.write("\n};\n\n");

  function dumpChunkIndexesTable(name, data /*: Array<bool> */) {
    output.write(
      `const Lexer::Unicode_Table_Chunk_Index_Type Lexer::${name}[] = {\n`
    );
    let chunkIndexes = chunk(data, chunkSize).map((c) =>
      chunkDataToIndex.get(boolsToBigInt(c))
    );
    chunkIndexes = rstripArray(chunkIndexes, zerosChunkIndex);
    output.write(dumpIntegerTableToString(chunkIndexes, { indentation: "  " }));
    console.log(`${name}_size = ${chunkIndexes.length}`);
    output.write("\n};\n");
    output.write(
      `static_assert(Lexer::${name}_size == sizeof(Lexer::${name}));\n`
    );
  }

  dumpChunkIndexesTable("identifier_start_chunk_indexes", identifierStartData);
  output.write("\n");
  dumpChunkIndexesTable("identifier_part_chunk_indexes", identifierPartData);

  output.write("}\n");

  output.write(
    `
// quick-lint-js finds bugs in JavaScript programs.
// Copyright (C) 2020  Matthew "strager" Glazar
//
// This file is part of quick-lint-js.
//
// quick-lint-js is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// quick-lint-js is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with quick-lint-js.  If not, see <https://www.gnu.org/licenses/>.
`
  );
}

// http://www.ecma-international.org/ecma-262/11.0/index.html#prod-IdentifierStart
function isJSIdentifierStart(codePoint) {
  return [0x24, 0x5f].includes(codePoint) || isIDStart(codePoint);
}

// http://www.ecma-international.org/ecma-262/11.0/index.html#prod-IdentifierPart
function isJSIdentifierPart(codePoint) {
  return [0x24, 0x200c, 0x200d].includes(codePoint) || isIDContinue(codePoint);
}

// https://www.unicode.org/reports/tr31/tr31-33.html
function isIDStart(codePoint) {
  return ID_START_CODE_POINTS.has(codePoint);
}

// https://www.unicode.org/reports/tr31/tr31-33.html
function isIDContinue(codePoint) {
  return ID_CONTINUE_CODE_POINTS.has(codePoint);
}

let ID_START_CODE_POINTS = new Set(
  require("@unicode/unicode-16.0.0/Binary_Property/ID_Start/code-points.js")
);
let ID_CONTINUE_CODE_POINTS = new Set(
  require("@unicode/unicode-16.0.0/Binary_Property/ID_Continue/code-points.js")
);

// Pattern_White_Space
// https://www.unicode.org/Public/15.1.0/ucd/PropList.txt
let PATTERN_WHITE_SPACE_CODE_POINTS = new Set([
  0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x0020, 0x0085, 0x200e, 0x200f,
  0x2028, 0x2029,
]);

// Other_ID_Start
// https://www.unicode.org/Public/15.1.0/ucd/PropList.txt
let OTHER_ID_START_CODE_POINTS = new Set([
  0x1885, 0x1886, 0x2118, 0x212e, 0x309b, 0x309c,
]);

// Other_ID_Continue
// https://www.unicode.org/Public/15.1.0/ucd/PropList.txt
let OTHER_ID_CONTINUE_CODE_POINTS = new Set([
  0x00b7, 0x0387, 0x1369, 0x136a, 0x136b, 0x136c, 0x136d, 0x136e, 0x136f,
  0x1370, 0x1371, 0x19da, 0x200c, 0x200d, 0x30fb, 0xff65,
]);

// Pattern_Syntax
// https://www.unicode.org/Public/15.1.0/ucd/PropList.txt
let PATTERN_SYNTAX_CODE_POINTS = new Set([
  0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029,
  0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x003a, 0x003b, 0x003c,
  0x003d, 0x003e, 0x003f, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060,
  0x007b, 0x007c, 0x007d, 0x007e, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5,
  0x00a6, 0x00a7, 0x00a9, 0x00ab, 0x00ac, 0x00ae, 0x00b0, 0x00b1, 0x00b6,
  0x00bb, 0x00bf, 0x00d7, 0x00f7, 0x2010, 0x2011, 0x2012, 0x2013, 0x2014,
  0x2015, 0x2016, 0x2017, 0x2018, 0x2019, 0x201a, 0x201b, 0x201c, 0x201d,
  0x201e, 0x201f, 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026,
  0x2027, 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037,
  0x2038, 0x2039, 0x203a, 0x203b, 0x203c, 0x203d, 0x203e, 0x2041, 0x2042,
  0x2043, 0x2044, 0x2045, 0x2046, 0x2047, 0x2048, 0x2049, 0x204a, 0x204b,
  0x204c, 0x204d, 0x204e, 0x204f, 0x2050, 0x2051, 0x2052, 0x2053, 0x2055,
  0x2056, 0x2057, 0x2058, 0x2059, 0x205a, 0x205b, 0x205c, 0x205d, 0x205e,
  0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, 0x2198,
  0x2199, 0x219a, 0x219b, 0x219c, 0x219d, 0x219e, 0x219f, 0x21a0, 0x21a1,
  0x21a2, 0x21a3, 0x21a4, 0x21a5, 0x21a6, 0x21a7, 0x21a8, 0x21a9, 0x21aa,
  0x21ab, 0x21ac, 0x21ad, 0x21ae, 0x21af, 0x21b0, 0x21b1, 0x21b2, 0x21b3,
  0x21b4, 0x21b5, 0x21b6, 0x21b7, 0x21b8, 0x21b9, 0x21ba, 0x21bb, 0x21bc,
  0x21bd, 0x21be, 0x21bf, 0x21c0, 0x21c1, 0x21c2, 0x21c3, 0x21c4, 0x21c5,
  0x21c6, 0x21c7, 0x21c8, 0x21c9, 0x21ca, 0x21cb, 0x21cc, 0x21cd, 0x21ce,
  0x21cf, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x21d5, 0x21d6, 0x21d7,
  0x21d8, 0x21d9, 0x21da, 0x21db, 0x21dc, 0x21dd, 0x21de, 0x21df, 0x21e0,
  0x21e1, 0x21e2, 0x21e3, 0x21e4, 0x21e5, 0x21e6, 0x21e7, 0x21e8, 0x21e9,
  0x21ea, 0x21eb, 0x21ec, 0x21ed, 0x21ee, 0x21ef, 0x21f0, 0x21f1, 0x21f2,
  0x21f3, 0x21f4, 0x21f5, 0x21f6, 0x21f7, 0x21f8, 0x21f9, 0x21fa, 0x21fb,
  0x21fc, 0x21fd, 0x21fe, 0x21ff, 0x2200, 0x2201, 0x2202, 0x2203, 0x2204,
  0x2205, 0x2206, 0x2207, 0x2208, 0x2209, 0x220a, 0x220b, 0x220c, 0x220d,
  0x220e, 0x220f, 0x2210, 0x2211, 0x2212, 0x2213, 0x2214, 0x2215, 0x2216,
  0x2217, 0x2218, 0x2219, 0x221a, 0x221b, 0x221c, 0x221d, 0x221e, 0x221f,
  0x2220, 0x2221, 0x2222, 0x2223, 0x2224, 0x2225, 0x2226, 0x2227, 0x2228,
  0x2229, 0x222a, 0x222b, 0x222c, 0x222d, 0x222e, 0x222f, 0x2230, 0x2231,
  0x2232, 0x2233, 0x2234, 0x2235, 0x2236, 0x2237, 0x2238, 0x2239, 0x223a,
  0x223b, 0x223c, 0x223d, 0x223e, 0x223f, 0x2240, 0x2241, 0x2242, 0x2243,
  0x2244, 0x2245, 0x2246, 0x2247, 0x2248, 0x2249, 0x224a, 0x224b, 0x224c,
  0x224d, 0x224e, 0x224f, 0x2250, 0x2251, 0x2252, 0x2253, 0x2254, 0x2255,
  0x2256, 0x2257, 0x2258, 0x2259, 0x225a, 0x225b, 0x225c, 0x225d, 0x225e,
  0x225f, 0x2260, 0x2261, 0x2262, 0x2263, 0x2264, 0x2265, 0x2266, 0x2267,
  0x2268, 0x2269, 0x226a, 0x226b, 0x226c, 0x226d, 0x226e, 0x226f, 0x2270,
  0x2271, 0x2272, 0x2273, 0x2274, 0x2275, 0x2276, 0x2277, 0x2278, 0x2279,
  0x227a, 0x227b, 0x227c, 0x227d, 0x227e, 0x227f, 0x2280, 0x2281, 0x2282,
  0x2283, 0x2284, 0x2285, 0x2286, 0x2287, 0x2288, 0x2289, 0x228a, 0x228b,
  0x228c, 0x228d, 0x228e, 0x228f, 0x2290, 0x2291, 0x2292, 0x2293, 0x2294,
  0x2295, 0x2296, 0x2297, 0x2298, 0x2299, 0x229a, 0x229b, 0x229c, 0x229d,
  0x229e, 0x229f, 0x22a0, 0x22a1, 0x22a2, 0x22a3, 0x22a4, 0x22a5, 0x22a6,
  0x22a7, 0x22a8, 0x22a9, 0x22aa, 0x22ab, 0x22ac, 0x22ad, 0x22ae, 0x22af,
  0x22b0, 0x22b1, 0x22b2, 0x22b3, 0x22b4, 0x22b5, 0x22b6, 0x22b7, 0x22b8,
  0x22b9, 0x22ba, 0x22bb, 0x22bc, 0x22bd, 0x22be, 0x22bf, 0x22c0, 0x22c1,
  0x22c2, 0x22c3, 0x22c4, 0x22c5, 0x22c6, 0x22c7, 0x22c8, 0x22c9, 0x22ca,
  0x22cb, 0x22cc, 0x22cd, 0x22ce, 0x22cf, 0x22d0, 0x22d1, 0x22d2, 0x22d3,
  0x22d4, 0x22d5, 0x22d6, 0x22d7, 0x22d8, 0x22d9, 0x22da, 0x22db, 0x22dc,
  0x22dd, 0x22de, 0x22df, 0x22e0, 0x22e1, 0x22e2, 0x22e3, 0x22e4, 0x22e5,
  0x22e6, 0x22e7, 0x22e8, 0x22e9, 0x22ea, 0x22eb, 0x22ec, 0x22ed, 0x22ee,
  0x22ef, 0x22f0, 0x22f1, 0x22f2, 0x22f3, 0x22f4, 0x22f5, 0x22f6, 0x22f7,
  0x22f8, 0x22f9, 0x22fa, 0x22fb, 0x22fc, 0x22fd, 0x22fe, 0x22ff, 0x2300,
  0x2301, 0x2302, 0x2303, 0x2304, 0x2305, 0x2306, 0x2307, 0x2308, 0x2309,
  0x230a, 0x230b, 0x230c, 0x230d, 0x230e, 0x230f, 0x2310, 0x2311, 0x2312,
  0x2313, 0x2314, 0x2315, 0x2316, 0x2317, 0x2318, 0x2319, 0x231a, 0x231b,
  0x231c, 0x231d, 0x231e, 0x231f, 0x2320, 0x2321, 0x2322, 0x2323, 0x2324,
  0x2325, 0x2326, 0x2327, 0x2328, 0x2329, 0x232a, 0x232b, 0x232c, 0x232d,
  0x232e, 0x232f, 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336,
  0x2337, 0x2338, 0x2339, 0x233a, 0x233b, 0x233c, 0x233d, 0x233e, 0x233f,
  0x2340, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, 0x2347, 0x2348,
  0x2349, 0x234a, 0x234b, 0x234c, 0x234d, 0x234e, 0x234f, 0x2350, 0x2351,
  0x2352, 0x2353, 0x2354, 0x2355, 0x2356, 0x2357, 0x2358, 0x2359, 0x235a,
  0x235b, 0x235c, 0x235d, 0x235e, 0x235f, 0x2360, 0x2361, 0x2362, 0x2363,
  0x2364, 0x2365, 0x2366, 0x2367, 0x2368, 0x2369, 0x236a, 0x236b, 0x236c,
  0x236d, 0x236e, 0x236f, 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375,
  0x2376, 0x2377, 0x2378, 0x2379, 0x237a, 0x237b, 0x237c, 0x237d, 0x237e,
  0x237f, 0x2380, 0x2381, 0x2382, 0x2383, 0x2384, 0x2385, 0x2386, 0x2387,
  0x2388, 0x2389, 0x238a, 0x238b, 0x238c, 0x238d, 0x238e, 0x238f, 0x2390,
  0x2391, 0x2392, 0x2393, 0x2394, 0x2395, 0x2396, 0x2397, 0x2398, 0x2399,
  0x239a, 0x239b, 0x239c, 0x239d, 0x239e, 0x239f, 0x23a0, 0x23a1, 0x23a2,
  0x23a3, 0x23a4, 0x23a5, 0x23a6, 0x23a7, 0x23a8, 0x23a9, 0x23aa, 0x23ab,
  0x23ac, 0x23ad, 0x23ae, 0x23af, 0x23b0, 0x23b1, 0x23b2, 0x23b3, 0x23b4,
  0x23b5, 0x23b6, 0x23b7, 0x23b8, 0x23b9, 0x23ba, 0x23bb, 0x23bc, 0x23bd,
  0x23be, 0x23bf, 0x23c0, 0x23c1, 0x23c2, 0x23c3, 0x23c4, 0x23c5, 0x23c6,
  0x23c7, 0x23c8, 0x23c9, 0x23ca, 0x23cb, 0x23cc, 0x23cd, 0x23ce, 0x23cf,
  0x23d0, 0x23d1, 0x23d2, 0x23d3, 0x23d4, 0x23d5, 0x23d6, 0x23d7, 0x23d8,
  0x23d9, 0x23da, 0x23db, 0x23dc, 0x23dd, 0x23de, 0x23df, 0x23e0, 0x23e1,
  0x23e2, 0x23e3, 0x23e4, 0x23e5, 0x23e6, 0x23e7, 0x23e8, 0x23e9, 0x23ea,
  0x23eb, 0x23ec, 0x23ed, 0x23ee, 0x23ef, 0x23f0, 0x23f1, 0x23f2, 0x23f3,
  0x23f4, 0x23f5, 0x23f6, 0x23f7, 0x23f8, 0x23f9, 0x23fa, 0x23fb, 0x23fc,
  0x23fd, 0x23fe, 0x23ff, 0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405,
  0x2406, 0x2407, 0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d, 0x240e,
  0x240f, 0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417,
  0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f, 0x2420,
  0x2421, 0x2422, 0x2423, 0x2424, 0x2425, 0x2426, 0x2427, 0x2428, 0x2429,
  0x242a, 0x242b, 0x242c, 0x242d, 0x242e, 0x242f, 0x2430, 0x2431, 0x2432,
  0x2433, 0x2434, 0x2435, 0x2436, 0x2437, 0x2438, 0x2439, 0x243a, 0x243b,
  0x243c, 0x243d, 0x243e, 0x243f, 0x2440, 0x2441, 0x2442, 0x2443, 0x2444,
  0x2445, 0x2446, 0x2447, 0x2448, 0x2449, 0x244a, 0x244b, 0x244c, 0x244d,
  0x244e, 0x244f, 0x2450, 0x2451, 0x2452, 0x2453, 0x2454, 0x2455, 0x2456,
  0x2457, 0x2458, 0x2459, 0x245a, 0x245b, 0x245c, 0x245d, 0x245e, 0x245f,
  0x2500, 0x2501, 0x2502, 0x2503, 0x2504, 0x2505, 0x2506, 0x2507, 0x2508,
  0x2509, 0x250a, 0x250b, 0x250c, 0x250d, 0x250e, 0x250f, 0x2510, 0x2511,
  0x2512, 0x2513, 0x2514, 0x2515, 0x2516, 0x2517, 0x2518, 0x2519, 0x251a,
  0x251b, 0x251c, 0x251d, 0x251e, 0x251f, 0x2520, 0x2521, 0x2522, 0x2523,
  0x2524, 0x2525, 0x2526, 0x2527, 0x2528, 0x2529, 0x252a, 0x252b, 0x252c,
  0x252d, 0x252e, 0x252f, 0x2530, 0x2531, 0x2532, 0x2533, 0x2534, 0x2535,
  0x2536, 0x2537, 0x2538, 0x2539, 0x253a, 0x253b, 0x253c, 0x253d, 0x253e,
  0x253f, 0x2540, 0x2541, 0x2542, 0x2543, 0x2544, 0x2545, 0x2546, 0x2547,
  0x2548, 0x2549, 0x254a, 0x254b, 0x254c, 0x254d, 0x254e, 0x254f, 0x2550,
  0x2551, 0x2552, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559,
  0x255a, 0x255b, 0x255c, 0x255d, 0x255e, 0x255f, 0x2560, 0x2561, 0x2562,
  0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x256b,
  0x256c, 0x256d, 0x256e, 0x256f, 0x2570, 0x2571, 0x2572, 0x2573, 0x2574,
  0x2575, 0x2576, 0x2577, 0x2578, 0x2579, 0x257a, 0x257b, 0x257c, 0x257d,
  0x257e, 0x257f, 0x2580, 0x2581, 0x2582, 0x2583, 0x2584, 0x2585, 0x2586,
  0x2587, 0x2588, 0x2589, 0x258a, 0x258b, 0x258c, 0x258d, 0x258e, 0x258f,
  0x2590, 0x2591, 0x2592, 0x2593, 0x2594, 0x2595, 0x2596, 0x2597, 0x2598,
  0x2599, 0x259a, 0x259b, 0x259c, 0x259d, 0x259e, 0x259f, 0x25a0, 0x25a1,
  0x25a2, 0x25a3, 0x25a4, 0x25a5, 0x25a6, 0x25a7, 0x25a8, 0x25a9, 0x25aa,
  0x25ab, 0x25ac, 0x25ad, 0x25ae, 0x25af, 0x25b0, 0x25b1, 0x25b2, 0x25b3,
  0x25b4, 0x25b5, 0x25b6, 0x25b7, 0x25b8, 0x25b9, 0x25ba, 0x25bb, 0x25bc,
  0x25bd, 0x25be, 0x25bf, 0x25c0, 0x25c1, 0x25c2, 0x25c3, 0x25c4, 0x25c5,
  0x25c6, 0x25c7, 0x25c8, 0x25c9, 0x25ca, 0x25cb, 0x25cc, 0x25cd, 0x25ce,
  0x25cf, 0x25d0, 0x25d1, 0x25d2, 0x25d3, 0x25d4, 0x25d5, 0x25d6, 0x25d7,
  0x25d8, 0x25d9, 0x25da, 0x25db, 0x25dc, 0x25dd, 0x25de, 0x25df, 0x25e0,
  0x25e1, 0x25e2, 0x25e3, 0x25e4, 0x25e5, 0x25e6, 0x25e7, 0x25e8, 0x25e9,
  0x25ea, 0x25eb, 0x25ec, 0x25ed, 0x25ee, 0x25ef, 0x25f0, 0x25f1, 0x25f2,
  0x25f3, 0x25f4, 0x25f5, 0x25f6, 0x25f7, 0x25f8, 0x25f9, 0x25fa, 0x25fb,
  0x25fc, 0x25fd, 0x25fe, 0x25ff, 0x2600, 0x2601, 0x2602, 0x2603, 0x2604,
  0x2605, 0x2606, 0x2607, 0x2608, 0x2609, 0x260a, 0x260b, 0x260c, 0x260d,
  0x260e, 0x260f, 0x2610, 0x2611, 0x2612, 0x2613, 0x2614, 0x2615, 0x2616,
  0x2617, 0x2618, 0x2619, 0x261a, 0x261b, 0x261c, 0x261d, 0x261e, 0x261f,
  0x2620, 0x2621, 0x2622, 0x2623, 0x2624, 0x2625, 0x2626, 0x2627, 0x2628,
  0x2629, 0x262a, 0x262b, 0x262c, 0x262d, 0x262e, 0x262f, 0x2630, 0x2631,
  0x2632, 0x2633, 0x2634, 0x2635, 0x2636, 0x2637, 0x2638, 0x2639, 0x263a,
  0x263b, 0x263c, 0x263d, 0x263e, 0x263f, 0x2640, 0x2641, 0x2642, 0x2643,
  0x2644, 0x2645, 0x2646, 0x2647, 0x2648, 0x2649, 0x264a, 0x264b, 0x264c,
  0x264d, 0x264e, 0x264f, 0x2650, 0x2651, 0x2652, 0x2653, 0x2654, 0x2655,
  0x2656, 0x2657, 0x2658, 0x2659, 0x265a, 0x265b, 0x265c, 0x265d, 0x265e,
  0x265f, 0x2660, 0x2661, 0x2662, 0x2663, 0x2664, 0x2665, 0x2666, 0x2667,
  0x2668, 0x2669, 0x266a, 0x266b, 0x266c, 0x266d, 0x266e, 0x266f, 0x2670,
  0x2671, 0x2672, 0x2673, 0x2674, 0x2675, 0x2676, 0x2677, 0x2678, 0x2679,
  0x267a, 0x267b, 0x267c, 0x267d, 0x267e, 0x267f, 0x2680, 0x2681, 0x2682,
  0x2683, 0x2684, 0x2685, 0x2686, 0x2687, 0x2688, 0x2689, 0x268a, 0x268b,
  0x268c, 0x268d, 0x268e, 0x268f, 0x2690, 0x2691, 0x2692, 0x2693, 0x2694,
  0x2695, 0x2696, 0x2697, 0x2698, 0x2699, 0x269a, 0x269b, 0x269c, 0x269d,
  0x269e, 0x269f, 0x26a0, 0x26a1, 0x26a2, 0x26a3, 0x26a4, 0x26a5, 0x26a6,
  0x26a7, 0x26a8, 0x26a9, 0x26aa, 0x26ab, 0x26ac, 0x26ad, 0x26ae, 0x26af,
  0x26b0, 0x26b1, 0x26b2, 0x26b3, 0x26b4, 0x26b5, 0x26b6, 0x26b7, 0x26b8,
  0x26b9, 0x26ba, 0x26bb, 0x26bc, 0x26bd, 0x26be, 0x26bf, 0x26c0, 0x26c1,
  0x26c2, 0x26c3, 0x26c4, 0x26c5, 0x26c6, 0x26c7, 0x26c8, 0x26c9, 0x26ca,
  0x26cb, 0x26cc, 0x26cd, 0x26ce, 0x26cf, 0x26d0, 0x26d1, 0x26d2, 0x26d3,
  0x26d4, 0x26d5, 0x26d6, 0x26d7, 0x26d8, 0x26d9, 0x26da, 0x26db, 0x26dc,
  0x26dd, 0x26de, 0x26df, 0x26e0, 0x26e1, 0x26e2, 0x26e3, 0x26e4, 0x26e5,
  0x26e6, 0x26e7, 0x26e8, 0x26e9, 0x26ea, 0x26eb, 0x26ec, 0x26ed, 0x26ee,
  0x26ef, 0x26f0, 0x26f1, 0x26f2, 0x26f3, 0x26f4, 0x26f5, 0x26f6, 0x26f7,
  0x26f8, 0x26f9, 0x26fa, 0x26fb, 0x26fc, 0x26fd, 0x26fe, 0x26ff, 0x2700,
  0x2701, 0x2702, 0x2703, 0x2704, 0x2705, 0x2706, 0x2707, 0x2708, 0x2709,
  0x270a, 0x270b, 0x270c, 0x270d, 0x270e, 0x270f, 0x2710, 0x2711, 0x2712,
  0x2713, 0x2714, 0x2715, 0x2716, 0x2717, 0x2718, 0x2719, 0x271a, 0x271b,
  0x271c, 0x271d, 0x271e, 0x271f, 0x2720, 0x2721, 0x2722, 0x2723, 0x2724,
  0x2725, 0x2726, 0x2727, 0x2728, 0x2729, 0x272a, 0x272b, 0x272c, 0x272d,
  0x272e, 0x272f, 0x2730, 0x2731, 0x2732, 0x2733, 0x2734, 0x2735, 0x2736,
  0x2737, 0x2738, 0x2739, 0x273a, 0x273b, 0x273c, 0x273d, 0x273e, 0x273f,
  0x2740, 0x2741, 0x2742, 0x2743, 0x2744, 0x2745, 0x2746, 0x2747, 0x2748,
  0x2749, 0x274a, 0x274b, 0x274c, 0x274d, 0x274e, 0x274f, 0x2750, 0x2751,
  0x2752, 0x2753, 0x2754, 0x2755, 0x2756, 0x2757, 0x2758, 0x2759, 0x275a,
  0x275b, 0x275c, 0x275d, 0x275e, 0x275f, 0x2760, 0x2761, 0x2762, 0x2763,
  0x2764, 0x2765, 0x2766, 0x2767, 0x2768, 0x2769, 0x276a, 0x276b, 0x276c,
  0x276d, 0x276e, 0x276f, 0x2770, 0x2771, 0x2772, 0x2773, 0x2774, 0x2775,
  0x2794, 0x2795, 0x2796, 0x2797, 0x2798, 0x2799, 0x279a, 0x279b, 0x279c,
  0x279d, 0x279e, 0x279f, 0x27a0, 0x27a1, 0x27a2, 0x27a3, 0x27a4, 0x27a5,
  0x27a6, 0x27a7, 0x27a8, 0x27a9, 0x27aa, 0x27ab, 0x27ac, 0x27ad, 0x27ae,
  0x27af, 0x27b0, 0x27b1, 0x27b2, 0x27b3, 0x27b4, 0x27b5, 0x27b6, 0x27b7,
  0x27b8, 0x27b9, 0x27ba, 0x27bb, 0x27bc, 0x27bd, 0x27be, 0x27bf, 0x27c0,
  0x27c1, 0x27c2, 0x27c3, 0x27c4, 0x27c5, 0x27c6, 0x27c7, 0x27c8, 0x27c9,
  0x27ca, 0x27cb, 0x27cc, 0x27cd, 0x27ce, 0x27cf, 0x27d0, 0x27d1, 0x27d2,
  0x27d3, 0x27d4, 0x27d5, 0x27d6, 0x27d7, 0x27d8, 0x27d9, 0x27da, 0x27db,
  0x27dc, 0x27dd, 0x27de, 0x27df, 0x27e0, 0x27e1, 0x27e2, 0x27e3, 0x27e4,
  0x27e5, 0x27e6, 0x27e7, 0x27e8, 0x27e9, 0x27ea, 0x27eb, 0x27ec, 0x27ed,
  0x27ee, 0x27ef, 0x27f0, 0x27f1, 0x27f2, 0x27f3, 0x27f4, 0x27f5, 0x27f6,
  0x27f7, 0x27f8, 0x27f9, 0x27fa, 0x27fb, 0x27fc, 0x27fd, 0x27fe, 0x27ff,
  0x2800, 0x2801, 0x2802, 0x2803, 0x2804, 0x2805, 0x2806, 0x2807, 0x2808,
  0x2809, 0x280a, 0x280b, 0x280c, 0x280d, 0x280e, 0x280f, 0x2810, 0x2811,
  0x2812, 0x2813, 0x2814, 0x2815, 0x2816, 0x2817, 0x2818, 0x2819, 0x281a,
  0x281b, 0x281c, 0x281d, 0x281e, 0x281f, 0x2820, 0x2821, 0x2822, 0x2823,
  0x2824, 0x2825, 0x2826, 0x2827, 0x2828, 0x2829, 0x282a, 0x282b, 0x282c,
  0x282d, 0x282e, 0x282f, 0x2830, 0x2831, 0x2832, 0x2833, 0x2834, 0x2835,
  0x2836, 0x2837, 0x2838, 0x2839, 0x283a, 0x283b, 0x283c, 0x283d, 0x283e,
  0x283f, 0x2840, 0x2841, 0x2842, 0x2843, 0x2844, 0x2845, 0x2846, 0x2847,
  0x2848, 0x2849, 0x284a, 0x284b, 0x284c, 0x284d, 0x284e, 0x284f, 0x2850,
  0x2851, 0x2852, 0x2853, 0x2854, 0x2855, 0x2856, 0x2857, 0x2858, 0x2859,
  0x285a, 0x285b, 0x285c, 0x285d, 0x285e, 0x285f, 0x2860, 0x2861, 0x2862,
  0x2863, 0x2864, 0x2865, 0x2866, 0x2867, 0x2868, 0x2869, 0x286a, 0x286b,
  0x286c, 0x286d, 0x286e, 0x286f, 0x2870, 0x2871, 0x2872, 0x2873, 0x2874,
  0x2875, 0x2876, 0x2877, 0x2878, 0x2879, 0x287a, 0x287b, 0x287c, 0x287d,
  0x287e, 0x287f, 0x2880, 0x2881, 0x2882, 0x2883, 0x2884, 0x2885, 0x2886,
  0x2887, 0x2888, 0x2889, 0x288a, 0x288b, 0x288c, 0x288d, 0x288e, 0x288f,
  0x2890, 0x2891, 0x2892, 0x2893, 0x2894, 0x2895, 0x2896, 0x2897, 0x2898,
  0x2899, 0x289a, 0x289b, 0x289c, 0x289d, 0x289e, 0x289f, 0x28a0, 0x28a1,
  0x28a2, 0x28a3, 0x28a4, 0x28a5, 0x28a6, 0x28a7, 0x28a8, 0x28a9, 0x28aa,
  0x28ab, 0x28ac, 0x28ad, 0x28ae, 0x28af, 0x28b0, 0x28b1, 0x28b2, 0x28b3,
  0x28b4, 0x28b5, 0x28b6, 0x28b7, 0x28b8, 0x28b9, 0x28ba, 0x28bb, 0x28bc,
  0x28bd, 0x28be, 0x28bf, 0x28c0, 0x28c1, 0x28c2, 0x28c3, 0x28c4, 0x28c5,
  0x28c6, 0x28c7, 0x28c8, 0x28c9, 0x28ca, 0x28cb, 0x28cc, 0x28cd, 0x28ce,
  0x28cf, 0x28d0, 0x28d1, 0x28d2, 0x28d3, 0x28d4, 0x28d5, 0x28d6, 0x28d7,
  0x28d8, 0x28d9, 0x28da, 0x28db, 0x28dc, 0x28dd, 0x28de, 0x28df, 0x28e0,
  0x28e1, 0x28e2, 0x28e3, 0x28e4, 0x28e5, 0x28e6, 0x28e7, 0x28e8, 0x28e9,
  0x28ea, 0x28eb, 0x28ec, 0x28ed, 0x28ee, 0x28ef, 0x28f0, 0x28f1, 0x28f2,
  0x28f3, 0x28f4, 0x28f5, 0x28f6, 0x28f7, 0x28f8, 0x28f9, 0x28fa, 0x28fb,
  0x28fc, 0x28fd, 0x28fe, 0x28ff, 0x2900, 0x2901, 0x2902, 0x2903, 0x2904,
  0x2905, 0x2906, 0x2907, 0x2908, 0x2909, 0x290a, 0x290b, 0x290c, 0x290d,
  0x290e, 0x290f, 0x2910, 0x2911, 0x2912, 0x2913, 0x2914, 0x2915, 0x2916,
  0x2917, 0x2918, 0x2919, 0x291a, 0x291b, 0x291c, 0x291d, 0x291e, 0x291f,
  0x2920, 0x2921, 0x2922, 0x2923, 0x2924, 0x2925, 0x2926, 0x2927, 0x2928,
  0x2929, 0x292a, 0x292b, 0x292c, 0x292d, 0x292e, 0x292f, 0x2930, 0x2931,
  0x2932, 0x2933, 0x2934, 0x2935, 0x2936, 0x2937, 0x2938, 0x2939, 0x293a,
  0x293b, 0x293c, 0x293d, 0x293e, 0x293f, 0x2940, 0x2941, 0x2942, 0x2943,
  0x2944, 0x2945, 0x2946, 0x2947, 0x2948, 0x2949, 0x294a, 0x294b, 0x294c,
  0x294d, 0x294e, 0x294f, 0x2950, 0x2951, 0x2952, 0x2953, 0x2954, 0x2955,
  0x2956, 0x2957, 0x2958, 0x2959, 0x295a, 0x295b, 0x295c, 0x295d, 0x295e,
  0x295f, 0x2960, 0x2961, 0x2962, 0x2963, 0x2964, 0x2965, 0x2966, 0x2967,
  0x2968, 0x2969, 0x296a, 0x296b, 0x296c, 0x296d, 0x296e, 0x296f, 0x2970,
  0x2971, 0x2972, 0x2973, 0x2974, 0x2975, 0x2976, 0x2977, 0x2978, 0x2979,
  0x297a, 0x297b, 0x297c, 0x297d, 0x297e, 0x297f, 0x2980, 0x2981, 0x2982,
  0x2983, 0x2984, 0x2985, 0x2986, 0x2987, 0x2988, 0x2989, 0x298a, 0x298b,
  0x298c, 0x298d, 0x298e, 0x298f, 0x2990, 0x2991, 0x2992, 0x2993, 0x2994,
  0x2995, 0x2996, 0x2997, 0x2998, 0x2999, 0x299a, 0x299b, 0x299c, 0x299d,
  0x299e, 0x299f, 0x29a0, 0x29a1, 0x29a2, 0x29a3, 0x29a4, 0x29a5, 0x29a6,
  0x29a7, 0x29a8, 0x29a9, 0x29aa, 0x29ab, 0x29ac, 0x29ad, 0x29ae, 0x29af,
  0x29b0, 0x29b1, 0x29b2, 0x29b3, 0x29b4, 0x29b5, 0x29b6, 0x29b7, 0x29b8,
  0x29b9, 0x29ba, 0x29bb, 0x29bc, 0x29bd, 0x29be, 0x29bf, 0x29c0, 0x29c1,
  0x29c2, 0x29c3, 0x29c4, 0x29c5, 0x29c6, 0x29c7, 0x29c8, 0x29c9, 0x29ca,
  0x29cb, 0x29cc, 0x29cd, 0x29ce, 0x29cf, 0x29d0, 0x29d1, 0x29d2, 0x29d3,
  0x29d4, 0x29d5, 0x29d6, 0x29d7, 0x29d8, 0x29d9, 0x29da, 0x29db, 0x29dc,
  0x29dd, 0x29de, 0x29df, 0x29e0, 0x29e1, 0x29e2, 0x29e3, 0x29e4, 0x29e5,
  0x29e6, 0x29e7, 0x29e8, 0x29e9, 0x29ea, 0x29eb, 0x29ec, 0x29ed, 0x29ee,
  0x29ef, 0x29f0, 0x29f1, 0x29f2, 0x29f3, 0x29f4, 0x29f5, 0x29f6, 0x29f7,
  0x29f8, 0x29f9, 0x29fa, 0x29fb, 0x29fc, 0x29fd, 0x29fe, 0x29ff, 0x2a00,
  0x2a01, 0x2a02, 0x2a03, 0x2a04, 0x2a05, 0x2a06, 0x2a07, 0x2a08, 0x2a09,
  0x2a0a, 0x2a0b, 0x2a0c, 0x2a0d, 0x2a0e, 0x2a0f, 0x2a10, 0x2a11, 0x2a12,
  0x2a13, 0x2a14, 0x2a15, 0x2a16, 0x2a17, 0x2a18, 0x2a19, 0x2a1a, 0x2a1b,
  0x2a1c, 0x2a1d, 0x2a1e, 0x2a1f, 0x2a20, 0x2a21, 0x2a22, 0x2a23, 0x2a24,
  0x2a25, 0x2a26, 0x2a27, 0x2a28, 0x2a29, 0x2a2a, 0x2a2b, 0x2a2c, 0x2a2d,
  0x2a2e, 0x2a2f, 0x2a30, 0x2a31, 0x2a32, 0x2a33, 0x2a34, 0x2a35, 0x2a36,
  0x2a37, 0x2a38, 0x2a39, 0x2a3a, 0x2a3b, 0x2a3c, 0x2a3d, 0x2a3e, 0x2a3f,
  0x2a40, 0x2a41, 0x2a42, 0x2a43, 0x2a44, 0x2a45, 0x2a46, 0x2a47, 0x2a48,
  0x2a49, 0x2a4a, 0x2a4b, 0x2a4c, 0x2a4d, 0x2a4e, 0x2a4f, 0x2a50, 0x2a51,
  0x2a52, 0x2a53, 0x2a54, 0x2a55, 0x2a56, 0x2a57, 0x2a58, 0x2a59, 0x2a5a,
  0x2a5b, 0x2a5c, 0x2a5d, 0x2a5e, 0x2a5f, 0x2a60, 0x2a61, 0x2a62, 0x2a63,
  0x2a64, 0x2a65, 0x2a66, 0x2a67, 0x2a68, 0x2a69, 0x2a6a, 0x2a6b, 0x2a6c,
  0x2a6d, 0x2a6e, 0x2a6f, 0x2a70, 0x2a71, 0x2a72, 0x2a73, 0x2a74, 0x2a75,
  0x2a76, 0x2a77, 0x2a78, 0x2a79, 0x2a7a, 0x2a7b, 0x2a7c, 0x2a7d, 0x2a7e,
  0x2a7f, 0x2a80, 0x2a81, 0x2a82, 0x2a83, 0x2a84, 0x2a85, 0x2a86, 0x2a87,
  0x2a88, 0x2a89, 0x2a8a, 0x2a8b, 0x2a8c, 0x2a8d, 0x2a8e, 0x2a8f, 0x2a90,
  0x2a91, 0x2a92, 0x2a93, 0x2a94, 0x2a95, 0x2a96, 0x2a97, 0x2a98, 0x2a99,
  0x2a9a, 0x2a9b, 0x2a9c, 0x2a9d, 0x2a9e, 0x2a9f, 0x2aa0, 0x2aa1, 0x2aa2,
  0x2aa3, 0x2aa4, 0x2aa5, 0x2aa6, 0x2aa7, 0x2aa8, 0x2aa9, 0x2aaa, 0x2aab,
  0x2aac, 0x2aad, 0x2aae, 0x2aaf, 0x2ab0, 0x2ab1, 0x2ab2, 0x2ab3, 0x2ab4,
  0x2ab5, 0x2ab6, 0x2ab7, 0x2ab8, 0x2ab9, 0x2aba, 0x2abb, 0x2abc, 0x2abd,
  0x2abe, 0x2abf, 0x2ac0, 0x2ac1, 0x2ac2, 0x2ac3, 0x2ac4, 0x2ac5, 0x2ac6,
  0x2ac7, 0x2ac8, 0x2ac9, 0x2aca, 0x2acb, 0x2acc, 0x2acd, 0x2ace, 0x2acf,
  0x2ad0, 0x2ad1, 0x2ad2, 0x2ad3, 0x2ad4, 0x2ad5, 0x2ad6, 0x2ad7, 0x2ad8,
  0x2ad9, 0x2ada, 0x2adb, 0x2adc, 0x2add, 0x2ade, 0x2adf, 0x2ae0, 0x2ae1,
  0x2ae2, 0x2ae3, 0x2ae4, 0x2ae5, 0x2ae6, 0x2ae7, 0x2ae8, 0x2ae9, 0x2aea,
  0x2aeb, 0x2aec, 0x2aed, 0x2aee, 0x2aef, 0x2af0, 0x2af1, 0x2af2, 0x2af3,
  0x2af4, 0x2af5, 0x2af6, 0x2af7, 0x2af8, 0x2af9, 0x2afa, 0x2afb, 0x2afc,
  0x2afd, 0x2afe, 0x2aff, 0x2b00, 0x2b01, 0x2b02, 0x2b03, 0x2b04, 0x2b05,
  0x2b06, 0x2b07, 0x2b08, 0x2b09, 0x2b0a, 0x2b0b, 0x2b0c, 0x2b0d, 0x2b0e,
  0x2b0f, 0x2b10, 0x2b11, 0x2b12, 0x2b13, 0x2b14, 0x2b15, 0x2b16, 0x2b17,
  0x2b18, 0x2b19, 0x2b1a, 0x2b1b, 0x2b1c, 0x2b1d, 0x2b1e, 0x2b1f, 0x2b20,
  0x2b21, 0x2b22, 0x2b23, 0x2b24, 0x2b25, 0x2b26, 0x2b27, 0x2b28, 0x2b29,
  0x2b2a, 0x2b2b, 0x2b2c, 0x2b2d, 0x2b2e, 0x2b2f, 0x2b30, 0x2b31, 0x2b32,
  0x2b33, 0x2b34, 0x2b35, 0x2b36, 0x2b37, 0x2b38, 0x2b39, 0x2b3a, 0x2b3b,
  0x2b3c, 0x2b3d, 0x2b3e, 0x2b3f, 0x2b40, 0x2b41, 0x2b42, 0x2b43, 0x2b44,
  0x2b45, 0x2b46, 0x2b47, 0x2b48, 0x2b49, 0x2b4a, 0x2b4b, 0x2b4c, 0x2b4d,
  0x2b4e, 0x2b4f, 0x2b50, 0x2b51, 0x2b52, 0x2b53, 0x2b54, 0x2b55, 0x2b56,
  0x2b57, 0x2b58, 0x2b59, 0x2b5a, 0x2b5b, 0x2b5c, 0x2b5d, 0x2b5e, 0x2b5f,
  0x2b60, 0x2b61, 0x2b62, 0x2b63, 0x2b64, 0x2b65, 0x2b66, 0x2b67, 0x2b68,
  0x2b69, 0x2b6a, 0x2b6b, 0x2b6c, 0x2b6d, 0x2b6e, 0x2b6f, 0x2b70, 0x2b71,
  0x2b72, 0x2b73, 0x2b74, 0x2b75, 0x2b76, 0x2b77, 0x2b78, 0x2b79, 0x2b7a,
  0x2b7b, 0x2b7c, 0x2b7d, 0x2b7e, 0x2b7f, 0x2b80, 0x2b81, 0x2b82, 0x2b83,
  0x2b84, 0x2b85, 0x2b86, 0x2b87, 0x2b88, 0x2b89, 0x2b8a, 0x2b8b, 0x2b8c,
  0x2b8d, 0x2b8e, 0x2b8f, 0x2b90, 0x2b91, 0x2b92, 0x2b93, 0x2b94, 0x2b95,
  0x2b96, 0x2b97, 0x2b98, 0x2b99, 0x2b9a, 0x2b9b, 0x2b9c, 0x2b9d, 0x2b9e,
  0x2b9f, 0x2ba0, 0x2ba1, 0x2ba2, 0x2ba3, 0x2ba4, 0x2ba5, 0x2ba6, 0x2ba7,
  0x2ba8, 0x2ba9, 0x2baa, 0x2bab, 0x2bac, 0x2bad, 0x2bae, 0x2baf, 0x2bb0,
  0x2bb1, 0x2bb2, 0x2bb3, 0x2bb4, 0x2bb5, 0x2bb6, 0x2bb7, 0x2bb8, 0x2bb9,
  0x2bba, 0x2bbb, 0x2bbc, 0x2bbd, 0x2bbe, 0x2bbf, 0x2bc0, 0x2bc1, 0x2bc2,
  0x2bc3, 0x2bc4, 0x2bc5, 0x2bc6, 0x2bc7, 0x2bc8, 0x2bc9, 0x2bca, 0x2bcb,
  0x2bcc, 0x2bcd, 0x2bce, 0x2bcf, 0x2bd0, 0x2bd1, 0x2bd2, 0x2bd3, 0x2bd4,
  0x2bd5, 0x2bd6, 0x2bd7, 0x2bd8, 0x2bd9, 0x2bda, 0x2bdb, 0x2bdc, 0x2bdd,
  0x2bde, 0x2bdf, 0x2be0, 0x2be1, 0x2be2, 0x2be3, 0x2be4, 0x2be5, 0x2be6,
  0x2be7, 0x2be8, 0x2be9, 0x2bea, 0x2beb, 0x2bec, 0x2bed, 0x2bee, 0x2bef,
  0x2bf0, 0x2bf1, 0x2bf2, 0x2bf3, 0x2bf4, 0x2bf5, 0x2bf6, 0x2bf7, 0x2bf8,
  0x2bf9, 0x2bfa, 0x2bfb, 0x2bfc, 0x2bfd, 0x2bfe, 0x2bff, 0x2e00, 0x2e01,
  0x2e02, 0x2e03, 0x2e04, 0x2e05, 0x2e06, 0x2e07, 0x2e08, 0x2e09, 0x2e0a,
  0x2e0b, 0x2e0c, 0x2e0d, 0x2e0e, 0x2e0f, 0x2e10, 0x2e11, 0x2e12, 0x2e13,
  0x2e14, 0x2e15, 0x2e16, 0x2e17, 0x2e18, 0x2e19, 0x2e1a, 0x2e1b, 0x2e1c,
  0x2e1d, 0x2e1e, 0x2e1f, 0x2e20, 0x2e21, 0x2e22, 0x2e23, 0x2e24, 0x2e25,
  0x2e26, 0x2e27, 0x2e28, 0x2e29, 0x2e2a, 0x2e2b, 0x2e2c, 0x2e2d, 0x2e2e,
  0x2e2f, 0x2e30, 0x2e31, 0x2e32, 0x2e33, 0x2e34, 0x2e35, 0x2e36, 0x2e37,
  0x2e38, 0x2e39, 0x2e3a, 0x2e3b, 0x2e3c, 0x2e3d, 0x2e3e, 0x2e3f, 0x2e40,
  0x2e41, 0x2e42, 0x2e43, 0x2e44, 0x2e45, 0x2e46, 0x2e47, 0x2e48, 0x2e49,
  0x2e4a, 0x2e4b, 0x2e4c, 0x2e4d, 0x2e4e, 0x2e4f, 0x2e50, 0x2e51, 0x2e52,
  0x2e53, 0x2e54, 0x2e55, 0x2e56, 0x2e57, 0x2e58, 0x2e59, 0x2e5a, 0x2e5b,
  0x2e5c, 0x2e5d, 0x2e5e, 0x2e5f, 0x2e60, 0x2e61, 0x2e62, 0x2e63, 0x2e64,
  0x2e65, 0x2e66, 0x2e67, 0x2e68, 0x2e69, 0x2e6a, 0x2e6b, 0x2e6c, 0x2e6d,
  0x2e6e, 0x2e6f, 0x2e70, 0x2e71, 0x2e72, 0x2e73, 0x2e74, 0x2e75, 0x2e76,
  0x2e77, 0x2e78, 0x2e79, 0x2e7a, 0x2e7b, 0x2e7c, 0x2e7d, 0x2e7e, 0x2e7f,
  0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x300c, 0x300d,
  0x300e, 0x300f, 0x3010, 0x3011, 0x3012, 0x3013, 0x3014, 0x3015, 0x3016,
  0x3017, 0x3018, 0x3019, 0x301a, 0x301b, 0x301c, 0x301d, 0x301e, 0x301f,
  0x3020, 0x3030, 0xfd3e, 0xfd3f, 0xfe45, 0xfe46,
]);

function rstripArray(things, elementToStrip) {
  let i = things.length;
  while (i-- > 0) {
    if (things[i] !== elementToStrip) {
      return things.slice(0, i + 1);
    }
  }
  return things;
}

function testIsIDStart() {
  // L
  assert.ok(isIDStart(0x61)); // LATIN SMALL LETTER A (Lu)
  assert.ok(isIDStart(0x41)); // LATIN CAPITAL LETTER A (Ll)
  assert.ok(isIDStart(0x00aa)); // FEMININE ORDINAL INDICATOR (Lo)
  assert.ok(isIDStart(0x02b0)); // MODIFIER LETTER SMALL H (Lm)
  assert.ok(isIDStart(0x1f88)); // GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI (Lt)
  assert.ok(isIDStart(0x08be)); // ARABIC LETTER PEH WITH SMALL V (Lo)

  // Nl
  assert.ok(isIDStart(0x2160)); // ROMAN NUMERAL ONE (Nl)

  // Other_ID_Start
  assert.ok(isIDStart(0x1885)); // MONGOLIAN LETTER ALI GALI BALUDA
  assert.ok(isIDStart(0x1886)); // MONGOLIAN LETTER ALI GALI THREE BALUDA
  assert.ok(isIDStart(0x2118)); // SCRIPT CAPITAL P
  assert.ok(isIDStart(0x212e)); // ESTIMATED SYMBOL
  assert.ok(isIDStart(0x309b)); // KATAKANA-HIRAGANA VOICED SOUND MARK
  assert.ok(isIDStart(0x309c)); // KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK

  assert.ok(isIDStart(0x2ee5d));

  // Pattern_Syntax (disallowed)
  for (let codePoint of PATTERN_SYNTAX_CODE_POINTS) {
    assert.ok(!isIDStart(codePoint), codePoint.toString(16));
  }

  // Pattern_White_Space (disallowed)
  for (let codePoint of PATTERN_WHITE_SPACE_CODE_POINTS) {
    assert.ok(!isIDStart(codePoint), codePoint.toString(16));
  }

  // Not included or explicitly excluded:
  assert.ok(!isIDStart(0x2460)); // CIRCLED DIGIT ONE (No)
  assert.ok(!isIDStart(0x005f)); // LOW LINE (Pc)
  assert.ok(!isIDStart(0x203f)); // UNDERTIE (Pc)
  assert.ok(!isIDStart(0x1400)); // CANADIAN SYLLABICS HYPHEN (Pd)
  assert.ok(!isIDStart(0x20a0)); // EURO-CURRENCY SIGN (Sc)
  assert.ok(!isIDStart(0x0024)); // DOLLAR SIGN (Sc)
  assert.ok(!isIDStart(0x20dd)); // COMBINING ENCLOSING CIRCLE (Me)
  assert.ok(!isIDStart(0x20f0)); // COMBINING ASTERISK ABOVE (Mn)
  assert.ok(!isIDStart(0xa627)); // VAI DIGIT SEVEN (Nd)
  assert.ok(!isIDStart(0x200c)); // ZERO WIDTH NON-JOINER (Cf)
  assert.ok(!isIDStart(0x200d)); // ZERO WIDTH JOINER (Cf)
  assert.ok(!isIDStart(0x202c)); // POP DIRECTIONAL FORMATTING (Cf)
  for (let codePoint of OTHER_ID_CONTINUE_CODE_POINTS) {
    assert.ok(!isIDStart(codePoint), codePoint.toString(16));
  }
}

function testIsIDContinue() {
  // L
  assert.ok(isIDContinue(0x61)); // LATIN SMALL LETTER A (Lu)
  assert.ok(isIDContinue(0x41)); // LATIN CAPITAL LETTER A (Ll)
  assert.ok(isIDContinue(0x00aa)); // FEMININE ORDINAL INDICATOR (Lo)
  assert.ok(isIDContinue(0x02b0)); // MODIFIER LETTER SMALL H (Lm)
  assert.ok(isIDContinue(0x1f88)); // GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI (Lt)

  // Nl
  assert.ok(isIDContinue(0x2160)); // ROMAN NUMERAL ONE (Nl)

  // Other_ID_Start
  assert.ok(isIDContinue(0x1885)); // MONGOLIAN LETTER ALI GALI BALUDA
  assert.ok(isIDContinue(0x1886)); // MONGOLIAN LETTER ALI GALI THREE BALUDA
  assert.ok(isIDContinue(0x2118)); // SCRIPT CAPITAL P
  assert.ok(isIDContinue(0x212e)); // ESTIMATED SYMBOL
  assert.ok(isIDContinue(0x309b)); // KATAKANA-HIRAGANA VOICED SOUND MARK
  assert.ok(isIDContinue(0x309c)); // KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK

  // Mn
  assert.ok(isIDContinue(0x20f0)); // COMBINING ASTERISK ABOVE (Mn)

  // Mc
  assert.ok(isIDContinue(0x1c26)); // LEPCHA VOWEL SIGN AA (Mc)

  // Nd
  assert.ok(isIDContinue(0xa627)); // VAI DIGIT SEVEN (Nd)

  // Pc
  assert.ok(isIDContinue(0x005f)); // LOW LINE (Pc)
  assert.ok(isIDContinue(0x203f)); // UNDERTIE (Pc)

  // Other_ID_Continue
  for (let codePoint in OTHER_ID_CONTINUE_CODE_POINTS) {
    assert.ok(isIDContinue(codePoint), codePoint.toString(16));
  }

  // Pattern_Syntax (disallowed)
  for (let codePoint of PATTERN_SYNTAX_CODE_POINTS) {
    assert.ok(!isIDContinue(codePoint), codePoint.toString(16));
  }

  // Pattern_White_Space (disallowed)
  for (let codePoint of PATTERN_WHITE_SPACE_CODE_POINTS) {
    assert.ok(!isIDContinue(codePoint), codePoint.toString(16));
  }

  // Not included or explicitly excluded:
  assert.ok(!isIDContinue(0x2460)); // CIRCLED DIGIT ONE (No)
  assert.ok(!isIDContinue(0x1400)); // CANADIAN SYLLABICS HYPHEN (Pd)
  assert.ok(!isIDContinue(0x20a0)); // EURO-CURRENCY SIGN (Sc)
  assert.ok(!isIDContinue(0x0024)); // DOLLAR SIGN (Sc)
  assert.ok(!isIDContinue(0x20dd)); // COMBINING ENCLOSING CIRCLE (Me)
  assert.ok(!isIDContinue(0x202c)); // POP DIRECTIONAL FORMATTING (Cf)
}

function testIsJSIdentifierStart() {
  // Extra allowed characters
  assert.ok(isJSIdentifierStart(0x0024)); // DOLLAR SIGN (Sc)
  assert.ok(isJSIdentifierStart(0x005f)); // LOW LINE (Pc)

  // L
  assert.ok(isJSIdentifierStart(0x61)); // LATIN SMALL LETTER A (Lu)
  assert.ok(isJSIdentifierStart(0x41)); // LATIN CAPITAL LETTER A (Ll)
  assert.ok(isJSIdentifierStart(0x00aa)); // FEMININE ORDINAL INDICATOR (Lo)
  assert.ok(isJSIdentifierStart(0x02b0)); // MODIFIER LETTER SMALL H (Lm)
  assert.ok(isJSIdentifierStart(0x1f88)); // GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI (Lt)

  // Nl
  assert.ok(isJSIdentifierStart(0x2160)); // ROMAN NUMERAL ONE (Nl)

  // Other_ID_Start
  assert.ok(isJSIdentifierStart(0x1885)); // MONGOLIAN LETTER ALI GALI BALUDA
  assert.ok(isJSIdentifierStart(0x1886)); // MONGOLIAN LETTER ALI GALI THREE BALUDA
  assert.ok(isJSIdentifierStart(0x2118)); // SCRIPT CAPITAL P
  assert.ok(isJSIdentifierStart(0x212e)); // ESTIMATED SYMBOL
  assert.ok(isJSIdentifierStart(0x309b)); // KATAKANA-HIRAGANA VOICED SOUND MARK
  assert.ok(isJSIdentifierStart(0x309c)); // KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK

  // Pattern_Syntax (disallowed, except for DOLLAR SIGN)
  for (let codePoint of PATTERN_SYNTAX_CODE_POINTS) {
    if (codePoint !== "$".charCodeAt(0)) {
      assert.ok(!isJSIdentifierStart(codePoint), codePoint.toString(16));
    }
  }

  // Pattern_White_Space (disallowed)
  for (let codePoint of PATTERN_WHITE_SPACE_CODE_POINTS) {
    assert.ok(!isJSIdentifierStart(codePoint), codePoint.toString(16));
  }

  // Not included or explicitly excluded:
  assert.ok(!isJSIdentifierStart(0x2460)); // CIRCLED DIGIT ONE (No)
  assert.ok(!isJSIdentifierStart(0x203f)); // UNDERTIE (Pc)
  assert.ok(!isJSIdentifierStart(0x1400)); // CANADIAN SYLLABICS HYPHEN (Pd)
  assert.ok(!isJSIdentifierStart(0x20a0)); // EURO-CURRENCY SIGN (Sc)
  assert.ok(!isJSIdentifierStart(0x20dd)); // COMBINING ENCLOSING CIRCLE (Me)
  assert.ok(!isJSIdentifierStart(0x20f0)); // COMBINING ASTERISK ABOVE (Mn)
  assert.ok(!isJSIdentifierStart(0xa627)); // VAI DIGIT SEVEN (Nd)
  assert.ok(!isJSIdentifierStart(0x200c)); // ZERO WIDTH NON-JOINER (Cf)
  assert.ok(!isJSIdentifierStart(0x200d)); // ZERO WIDTH JOINER (Cf)
  assert.ok(!isJSIdentifierStart(0x202c)); // POP DIRECTIONAL FORMATTING (Cf)
  for (let codePoint of OTHER_ID_CONTINUE_CODE_POINTS) {
    assert.ok(!isJSIdentifierStart(codePoint), codePoint.toString(16));
  }

  // https://mathiasbynens.be/notes/javascript-identifiers-es6
  assert.ok(isJSIdentifierStart(0x102a7)); // CARIAN LETTER A2
  assert.ok(!isJSIdentifierStart(0xd800)); // CARIAN LETTER A2 first surrogate
  assert.ok(!isJSIdentifierStart(0xdea7)); // CARIAN LETTER A2 second surrogate
}

function testIsJSIdentifierPart() {
  // Extra allowed characters
  assert.ok(isJSIdentifierPart(0x0024)); // DOLLAR SIGN (Sc)
  assert.ok(isJSIdentifierPart(0x200c)); // ZERO WIDTH NON-JOINER (Cf)
  assert.ok(isJSIdentifierPart(0x200d)); // ZERO WIDTH JOINER (Cf)

  // L
  assert.ok(isJSIdentifierPart(0x61)); // LATIN SMALL LETTER A (Lu)
  assert.ok(isJSIdentifierPart(0x41)); // LATIN CAPITAL LETTER A (Ll)
  assert.ok(isJSIdentifierPart(0x00aa)); // FEMININE ORDINAL INDICATOR (Lo)
  assert.ok(isJSIdentifierPart(0x02b0)); // MODIFIER LETTER SMALL H (Lm)
  assert.ok(isJSIdentifierPart(0x1f88)); // GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI (Lt)

  // Nl
  assert.ok(isJSIdentifierPart(0x2160)); // ROMAN NUMERAL ONE (Nl)

  // Other_ID_Start
  assert.ok(isJSIdentifierPart(0x1885)); // MONGOLIAN LETTER ALI GALI BALUDA
  assert.ok(isJSIdentifierPart(0x1886)); // MONGOLIAN LETTER ALI GALI THREE BALUDA
  assert.ok(isJSIdentifierPart(0x2118)); // SCRIPT CAPITAL P
  assert.ok(isJSIdentifierPart(0x212e)); // ESTIMATED SYMBOL
  assert.ok(isJSIdentifierPart(0x309b)); // KATAKANA-HIRAGANA VOICED SOUND MARK
  assert.ok(isJSIdentifierPart(0x309c)); // KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK

  // Mn
  assert.ok(isJSIdentifierPart(0x20f0)); // COMBINING ASTERISK ABOVE (Mn)

  // Mc
  assert.ok(isJSIdentifierPart(0x1c26)); // LEPCHA VOWEL SIGN AA (Mc)

  // Nd
  assert.ok(isJSIdentifierPart(0xa627)); // VAI DIGIT SEVEN (Nd)

  // Pc
  assert.ok(isJSIdentifierPart(0x005f)); // LOW LINE (Pc)
  assert.ok(isJSIdentifierPart(0x203f)); // UNDERTIE (Pc)

  // Other_ID_Continue
  for (let codePoint of OTHER_ID_CONTINUE_CODE_POINTS) {
    assert.ok(isJSIdentifierPart(codePoint), codePoint.toString(16));
  }

  // Pattern_Syntax (disallowed, except for DOLLAR SIGN)
  for (let codePoint of PATTERN_SYNTAX_CODE_POINTS) {
    if (codePoint !== "$".charCodeAt(0)) {
      assert.ok(!isJSIdentifierPart(codePoint), codePoint.toString(16));
    }
  }

  // Pattern_White_Space (disallowed)
  for (let codePoint of PATTERN_WHITE_SPACE_CODE_POINTS) {
    assert.ok(!isJSIdentifierPart(codePoint), codePoint.toString(16));
  }

  // Not included or explicitly excluded:
  assert.ok(!isJSIdentifierPart(0x2460)); // CIRCLED DIGIT ONE (No)
  assert.ok(!isJSIdentifierPart(0x1400)); // CANADIAN SYLLABICS HYPHEN (Pd)
  assert.ok(!isJSIdentifierPart(0x20a0)); // EURO-CURRENCY SIGN (Sc)
  assert.ok(!isJSIdentifierPart(0x20dd)); // COMBINING ENCLOSING CIRCLE (Me)
  assert.ok(!isJSIdentifierPart(0x202c)); // POP DIRECTIONAL FORMATTING (Cf)

  // https://mathiasbynens.be/notes/javascript-identifiers-es6
  assert.ok(isJSIdentifierStart(0x102a7)); // CARIAN LETTER A2
  assert.ok(!isJSIdentifierStart(0xd800)); // CARIAN LETTER A2 first surrogate
  assert.ok(!isJSIdentifierStart(0xdea7)); // CARIAN LETTER A2 second surrogate
}

function chunk(items, chunkSize) {
  let result = [];
  for (let i = 0; i < items.length; i += chunkSize) {
    result.push(items.slice(i, i + chunkSize));
  }
  return result;
}

function dumpBitTableToString(bits, { indentation = "" } = {}) {
  let bytes = [];
  for (let byteIndex = 0; byteIndex < bits.length; byteIndex += BITS_PER_BYTE) {
    let byte = 0;
    for (let bitInByte = 0; bitInByte < BITS_PER_BYTE; ++bitInByte) {
      let bitIndex = byteIndex + bitInByte;
      let bit = bitIndex < bits.length ? bits[bitIndex] : 0;
      byte |= bit << bitInByte;
    }
    bytes.push(byte);
  }
  return dumpIntegerTableToString(bytes, { indentation });
}

function dumpIntegerTableToString(
  integers,
  { integersPerLine = 8, indentation = "" } = {}
) {
  let out = "";
  let integersWrittenOnLine = 0;
  for (let integer of integers) {
    if (integersWrittenOnLine >= integersPerLine) {
      out += "\n";
      integersWrittenOnLine = 0;
    }
    if (integersWrittenOnLine > 0) {
      out += " ";
    } else {
      out += indentation;
    }
    out += `0x${integer.toString(16).padStart(2, "0")},`;
    integersWrittenOnLine += 1;
  }
  return out;
}

function testDumpBitTable() {
  assert.strictEqual(dumpBitTableToString([]), "");
  assert.strictEqual(dumpBitTableToString([1, 0, 1, 0, 1, 0, 1, 0]), "0x55,");
  assert.strictEqual(
    // prettier-ignore
    dumpBitTableToString([
      1, 1, 1, 1, 0, 0, 0, 0,
      0, 0, 0, 0, 1, 1, 1, 1,
    ]),
    "0x0f, 0xf0,"
  );

  // Dumping zero-pads missing bits:
  assert.strictEqual(dumpBitTableToString([1, 0, 1, 0, 1]), "0x15,");

  // Dumping should line break after 8 bytes:
  assert.strictEqual(
    dumpBitTableToString(
      [
        ...("10000000" + // 0x01
          "01000000" + // 0x02
          "11000000" + // 0x03
          "00100000" + // 0x04
          "10100000" + // 0x05
          "01100000" + // 0x06
          "11100000" + // 0x07
          "00010000" + // 0x08
          "10010000" + // 0x09
          "01010000" + // 0x0a
          "11010000" + // 0x0b
          "00110000" + // 0x0c
          "10110000" + // 0x0d
          "01110000" + // 0x0e
          "11110000" + // 0x0f
          "00001000" + // 0x10
          "10001000" + // 0x11
          "01001000" + // 0x12
          "11001000" + // 0x13
          "00101000"), // 0x14
      ].map((byte) => +byte)
    ),
    `\
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\n\
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,\n\
0x11, 0x12, 0x13, 0x14,`
  );

  // Dumping should indent at the beginning of each line:
  assert.strictEqual(
    dumpBitTableToString(
      [
        ...("10000000" + // 0x01
          "01000000" + // 0x02
          "11000000" + // 0x03
          "00100000" + // 0x04
          "10100000" + // 0x05
          "01100000" + // 0x06
          "11100000" + // 0x07
          "00010000" + // 0x08
          "10010000"), // 0x09
      ].map((byte) => +byte),
      { indentation: "...." }
    ),
    `\
....0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\n\
....0x09,`
  );
}

function testRstripArray() {
  assert.deepStrictEqual(rstripArray([], 3), []);
  assert.deepStrictEqual(rstripArray([1, 2], 3), [1, 2]);
  assert.deepStrictEqual(rstripArray([1, 2, 3], 3), [1, 2]);
  assert.deepStrictEqual(rstripArray([3, 2, 1], 3), [3, 2, 1]);
  assert.deepStrictEqual(rstripArray([1, 3, 1, 3, 3, 3, 3], 3), [1, 3, 1]);
}

main();

// quick-lint-js finds bugs in JavaScript programs.
// Copyright (C) 2020  Matthew "strager" Glazar
//
// This file is part of quick-lint-js.
//
// quick-lint-js is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// quick-lint-js is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with quick-lint-js.  If not, see <https://www.gnu.org/licenses/>.
